// Ticket Clerk Agent
//
// This function represents the logic and activities of a ticket clerk in the movie theater simulation.
// This function will be forked onto a thread during initialization.
//

#include "TicketClerkAgent.h"
#include "globalsExtern.cc"
#include "utility.h"

void TicketClerkAgentExecute(int arg) {
	// Ticket Clerk Agent local data goes here
	int TicketClerkID = arg;
	int numTicketsRequested;
	int totalPrice;
	int pricePaid;
	TicketStub* stub;

	// Ticket Clerk Agent running code should go here.
	DEBUG('T', "%s: Thread now running.\n", currentThread->getName());
	
	
	
	ticketClerkCustomerLock[TicketClerkID]->Acquire();
	ticketClerkCondition[TicketClerkID]->Wait(ticketClerkCustomerLock[TicketClerkID]);
	// Loop Invariant - Finished with last customer, starting a new conversation.
	while (true) {
	   // Wake up the next customer in our line and tell him to approach.
	   DEBUG('T', "%s: Calling next customer to my window.\n", currentThread->getName());
	   ticketLineSelectionLock->Acquire();
	   ticketLineSelectionCondition[TicketClerkID]->Signal(ticketLineSelectionLock);
	   ticketLineSelectionLock->Release();
	   ticketClerkCondition[TicketClerkID]->Wait(ticketClerkCustomerLock[TicketClerkID]);

	   // At this point the customer is waiting at our kiosk and has
	   // listed how many tickets they need.
	   totalsLock->Acquire();
	   
	   // To not oversell a show, make sure there are enough "availible tickets"
	   // before we sell more
	   while (totalsWhiteboard->getAvailableTickets() < ticketClerkCustomerWhiteboard[TicketClerkID]->getNumTickets()) {
		  DEBUG('R',"%s has stopped selling tickets.\n",currentThread->getName());
		  totalsTicketClerkCondition->Wait(totalsLock);
	   }
	   
	   // Ok to sell the tickets, updating "availible tickets"
	   totalsWhiteboard->incrementTicketRevenue(TicketClerkID, 12*ticketClerkCustomerWhiteboard[TicketClerkID]->getNumTickets());
	   totalsWhiteboard->setAvailableTickets(totalsWhiteboard->getAvailableTickets() - ticketClerkCustomerWhiteboard[TicketClerkID]->getNumTickets());
	   // Update stats on the totals lock (num sold, revenue, ect)
	   totalsLock->Release();
	   
	   // Post price to whiteboard
		numTicketsRequested = ticketClerkCustomerWhiteboard[TicketClerkID]->getNumTickets();
		totalPrice = numTicketsRequested*12;
		ticketClerkCustomerWhiteboard[TicketClerkID]->setTotalPrice(totalPrice);
		DEBUG('R', "%s has an order for %d tickets and the cost is %d.\n",currentThread->getName(),totalPrice,numTicketsRequested);
		DEBUG('T', "%s: Requesting $%d for the tickets. Woke up Customer.\n", currentThread->getName(), totalPrice);
		ticketCustomerCondition[TicketClerkID]->Signal(ticketClerkCustomerLock[TicketClerkID]);
		ticketClerkCondition[TicketClerkID]->Wait(ticketClerkCustomerLock[TicketClerkID]);
		
		// Customer responded with they amount they will pay and woke us up.
		pricePaid = ticketClerkCustomerWhiteboard[TicketClerkID]->getMoneyGiven();
		if (pricePaid != totalPrice) {
			DEBUG('T', "%s: ERROR: Customer did not pay correct price.\n", currentThread->getName());
		}
		else {
			DEBUG('T', "%s: Printed ticket stub.\n", currentThread->getName());
			stub = new TicketStub(numTicketsRequested, 1);
			ticketClerkCustomerWhiteboard[TicketClerkID]->setTicketStub(stub);
		}
		DEBUG('T', "%s: Woke up customer.\n", currentThread->getName());
		ticketCustomerCondition[TicketClerkID]->Signal(ticketClerkCustomerLock[TicketClerkID]);
		ticketClerkCondition[TicketClerkID]->Wait(ticketClerkCustomerLock[TicketClerkID]);

	   // At this point the customer has taken his tickets and left.
	   ClearTicketClerkCustomerWhiteboard(TicketClerkID);

	   // Need to check if our line is empty or not
	   // If it is empty we should sleep until a customer enters our line
	   // and awakens us. If a customer is there we should return to start
	   // of the loop.
	   ticketLineSelectionLock->Acquire();
	   
	   // Check if the line length is above or below a threshold and signal the manager that he might need to make a break decision
		if (ticketLineSelectionWhiteboard->getLineLength(TicketClerkID) < 3) {
			DEBUG('R', "%s has a line length %d and is signaling a customer.\n",currentThread->getName(),ticketLineSelectionWhiteboard->getLineLength(TicketClerkID));
			DEBUG('T', "%s: Informing manager that my line is too short.\n", currentThread->getName());
			managerLock->Acquire();
			managerWhiteboard->setTicketClerkLineStatus(TicketClerkID, managerWhiteboard->CLERK_LINE_SHORT);
			managerCondition->Signal(managerLock);
			managerLock->Release();
		}
		else if (ticketLineSelectionWhiteboard->getLineLength(TicketClerkID) > 5) {
			DEBUG('T', "%s: Informing manager that my line is too long.\n", currentThread->getName());
			managerLock->Acquire();
			managerWhiteboard->setTicketClerkLineStatus(TicketClerkID, managerWhiteboard->CLERK_LINE_FULL);
			managerCondition->Signal(managerLock);
			managerLock->Release();
		}
	   
	   if (ticketLineSelectionWhiteboard->getLineLength(TicketClerkID) == 0) {
		  DEBUG('R', "%s has no one in line. I am available for a customer.\n", currentThread->getName());
		  ticketLineSelectionLock->Release();
		  // There is no one in our line, sleeping
		  ticketClerkCondition[TicketClerkID]->Wait(ticketClerkCustomerLock[TicketClerkID]);
	   }
	   else {
		  ticketLineSelectionLock->Release();
	   }
	}
}

void ClearTicketClerkCustomerWhiteboard(int TicketClerkID) {
    DEBUG('T', "%s: Clearing whiteboard.\n", currentThread->getName());
	ticketClerkCustomerWhiteboard[TicketClerkID]->setNumTickets(0);
	ticketClerkCustomerWhiteboard[TicketClerkID]->setTotalPrice(0);
	ticketClerkCustomerWhiteboard[TicketClerkID]->setMoneyGiven(0);
	ticketClerkCustomerWhiteboard[TicketClerkID]->setTicketStub(NULL);
}
